package com.ssm.tool;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Component
@Slf4j
public class PropertiesUtils {

    /**
     * 属性文件 与 属性集合的映射
     */
    private Map<String, Properties> propertiesMap = new HashMap<>();

    /**
     * 属性文件 与 修改时间的映射
     */
    private Map<String, Long> modifyTimeMap = new HashMap<>();

    /**
     * 指定的属性文件的目录路径
     */
    private String configPath = "";

    public void setConfigPath(String path) {
        this.configPath = path;
    }

    /**
     * 单例模式 构造PropertiesUtils
     */
    private PropertiesUtils() {} //私有化，禁止外部空参构造

    public static class SingleHolder {
        private static PropertiesUtils instance = new PropertiesUtils();
    }

    public static PropertiesUtils getInstance() {
        return SingleHolder.instance;
    }

    /**
     * 获取属性文件中key对应的值
     * @param propertyFileName 属性文件名称
     * @param key 属性文件中属性的key
     */
    public String getPropertyValue(String propertyFileName, String key) {
        try {
            String fileName = covertFileName(propertyFileName);
            //如果map中还没有该属性文件，就加载进去
            if(propertiesMap.get(fileName) == null) {
                loadProperties(fileName);
            } else {
                //如果存在，就检查属性文件是否被修改
                checkPropertiesModified(fileName);
            }
            return propertiesMap.get(fileName).getProperty(key); //Properties本身也是属性集合
        } catch (Exception e) {
            log.error("PropertiesUtils.getPropertyValue.error:{}", e.getMessage(), e);
            return "";
        }
    }


    /**
     * 校验属性文件的名称(.properties结尾的，去掉后缀再返回)
     */
    private String covertFileName(String propertyFileName) {
        //不以.properties结尾的，直接返回
        if(!propertyFileName.endsWith(".properties")) {
            return propertyFileName;
        }
        //去掉后缀，返回
        int index = propertyFileName.indexOf(".");
        return propertyFileName.substring(0, index);
    }

    /**
     * 加载属性文件到propertiesMap集合中去
     */
    private void loadProperties(String fileName) throws URISyntaxException {
        //根据配置文件的名称，来获取相应配置文件
       File file = getPropertyFile(fileName);
       //获取文件最后一次的修改时间
        long newTime = file.lastModified();

        //二次判断--文件已存在属性文件集合时，把旧属性文件删除(判断文件是否被修改时也要使用该方法)
        if (propertiesMap.get(fileName) != null) {
            propertiesMap.remove(fileName);
        }

        Properties properties = new Properties();
        try {
            //从指定的文件中加载属性列表（键值对）到Properties对象中
            properties.load(new FileInputStream(file));
        } catch (Exception e) {
            log.error("PropertiesUtils.loadProperties.error:{}", e.getMessage(), e);
        }

        propertiesMap.put(fileName, properties);
        modifyTimeMap.put(fileName, newTime);
    }

    /**
     * 根据文件名称获取文件对象
     */
    private File getPropertyFile(String fileName) throws URISyntaxException {
        File propertiesFile = null;

        //1.如果 指定的属性文件的目录路径configPath不为空(提供了统一的属性文件存放目录)，则直接拼接
        if(!StringUtils.isBlank(configPath)) {
            return new File(this.configPath + File.separator + fileName + ".properties"); //File.separator == .
        }

        //2.如果configPath为空，那么就使用内置的user.dir路径(是一个系统属性，表示当前JVM的“用户工作目录”的绝对路径。这通常是启动Java应用程序的目录)
        String dir = System.getProperty("user.dir") + File.separator + fileName + ".properties";
        propertiesFile = new File(dir);

        //3. 如果内置路径没有文件，那么就从最终指定路径中查找
        if(!propertiesFile.exists()) {
            // 同级（编译路径）的resource根目录下，是否存在对应文件，存在则返回该地址
            URL url = PropertiesUtils.class.getResource("/" + fileName + ".properties");
            if(url != null) {
                propertiesFile = new File(url.getFile());
            }
        }
        return propertiesFile;
    }

    /**
     * 检查文件是否被修改
     */
    private void checkPropertiesModified(String fileName) throws URISyntaxException {
        //拿到file文件
        File file = getPropertyFile(fileName);
        //拿到文件最后一次修改时间
        long newTime = file.lastModified();

        //拿到该文件上次加载时的最后一次修改时间
        Long lastModifiedTime = modifyTimeMap.get(fileName);

        if(newTime == 0) { //文件不存在
            if(lastModifiedTime == null) {
                log.error(fileName + ".properties file does not exist!");
            }
        } else if(newTime > lastModifiedTime) {
            //文件更新，重新加载
            loadProperties(fileName);
        }
    }
}
